using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.AccessControl;

namespace CrowdMonitoring
{
    /// <summary>
    /// 融合版：摄像头围观检测 + 人员离开自动锁定系统
    /// </summary>
    public class CrowdAndLeaveMonitor : IDisposable
    {
        #region 配置参数（可按需调整）
        // 围观检测参数
        private const int _crowdMinPersonCount = 2;    // 构成围观的最少人数（默认2人）
        private const int _crowdMaxDistancePixel = 200;// 判定聚集的最大像素距离
        private readonly Size _crowdMinDetectSize = new Size(50, 50); // 人脸检测最小尺寸

        // 人员离开检测参数
        public int LeaveTimeoutMs { get; set; } = 60 * 1000; // 离开判定时间（默认15秒）
        private const int _leaveFrameInterval = 100;        // 帧处理间隔（100ms=10帧/秒）

        // 通用检测参数
        private const int _detectMinNeighbors = 3;     // 过滤误检的最小邻居数
        #endregion

        #region Windows API（系统锁定）
        /// <summary>
        /// 调用Windows API锁定工作站
        /// </summary>
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool LockWorkStation();
        #endregion

        #region 核心组件与状态
        // 检测器与摄像头
        private readonly CascadeClassifier _detector;   // Haar分类器（人脸/上半身）
        private readonly VideoCapture _capture;         // 摄像头捕获
        private bool _isMonitoring;                     // 监控运行状态
        private readonly object _lockObj = new object();// 线程安全锁

        // 围观检测状态
        private bool _isCrowded;                        // 当前是否围观
        private int _currentPersonCount;                // 当前检测到的人数

        // 人员离开检测状态
        private DateTime? _noPersonStartTime;           // 无人状态开始时间
        private bool _hasLockedSystem;                  // 是否已锁定系统（避免重复锁定）
        #endregion

        #region 事件回调（对外反馈两种状态）
        /// <summary>
        /// 围观状态变化时触发（参数：当前人数、是否围观）
        /// </summary>
        public event Action<int, bool>? CrowdStatusChanged;

        /// <summary>
        /// 人员离开状态变化时触发（参数：是否有人、无人持续时间（秒）、是否已锁定）
        /// </summary>
        public event Action<bool, double, bool>? LeaveStatusChanged;

        /// <summary>
        /// 错误发生时触发（参数：错误信息）
        /// </summary>
        public event Action<string>? ErrorOccurred;
        #endregion

        /// <summary>
        /// 构造函数（初始化检测器、摄像头）
        /// </summary>
        /// <param name="cameraIndex">摄像头索引（默认0）</param>
        /// <param name="haarCascadePath">Haar分类器路径（默认正脸检测器）</param>
        public CrowdAndLeaveMonitor(int cameraIndex = 0, string haarCascadePath = "haarcascade_frontalface_default.xml")
        {
            try
            {
                // 1. 验证并初始化Haar分类器
                if (!System.IO.File.Exists(haarCascadePath))
                    throw new System.IO.FileNotFoundException("Haar分类器文件未找到，请检查路径", haarCascadePath);
                _detector = new CascadeClassifier(haarCascadePath);

                // 2. 初始化摄像头并验证
                _capture = new VideoCapture(cameraIndex);
                if (!_capture.IsOpened)
                    throw new InvalidOperationException($"摄像头初始化失败！索引：{cameraIndex}（可能被其他程序占用）");

                // 3. 配置摄像头（低分辨率平衡性能）
                _capture.Set(CapProp.FrameWidth, 640);
                _capture.Set(CapProp.FrameHeight, 480);
                _capture.Set(CapProp.Fps, 10);

                Console.WriteLine("融合版监控初始化完成（含围观检测+15秒无人锁定）");
            }
            catch (Exception ex)
            {
                var errorMsg = $"初始化失败：{ex.Message}";
                ErrorOccurred?.Invoke(errorMsg);
                Console.WriteLine(errorMsg);
                throw;
            }
        }

        /// <summary>
        /// 开始监控（启动帧处理线程）
        /// </summary>
        public void StartMonitoring()
        {
            lock (_lockObj)
            {
                if (_isMonitoring)
                {
                    ErrorOccurred?.Invoke("监控已在运行，无需重复启动");
                    return;
                }

                // 重置所有状态
                _isMonitoring = true;
                _isCrowded = false;
                _currentPersonCount = 0;
                _noPersonStartTime = null;
                _hasLockedSystem = false;

                Console.WriteLine("开始监控（围观检测+人员离开锁定）...");

                // 启动帧处理线程（长时间任务，避免线程池回收）
                System.Threading.Tasks.Task.Factory.StartNew(
                    action: ProcessFramesLoop,
                    cancellationToken: System.Threading.CancellationToken.None,
                    creationOptions: System.Threading.Tasks.TaskCreationOptions.LongRunning,
                    scheduler: System.Threading.Tasks.TaskScheduler.Default
                );
            }
        }

        /// <summary>
        /// 停止监控
        /// </summary>
        public void StopMonitoring()
        {
            lock (_lockObj)
            {
                _isMonitoring = false;
                Console.WriteLine("已停止监控");
            }
        }

        /// <summary>
        /// 帧处理核心循环（一次帧处理=围观检测+离开判断）
        /// </summary>
        private void ProcessFramesLoop()
        {
            while (_isMonitoring)
            {
                try
                {
                    // 1. 读取摄像头帧
                    using (var frame = _capture.QueryFrame())
                    {
                        if (frame.IsEmpty) // 帧为空（摄像头断开）
                        {
                            System.Threading.Thread.Sleep(_leaveFrameInterval);
                            continue;
                        }

                        // 2. 帧预处理（灰度化+直方图均衡化，提升检测精度）
                        using (var grayFrame = new Mat())
                        {
                            CvInvoke.CvtColor(frame, grayFrame, ColorConversion.Bgr2Gray);
                            CvInvoke.EqualizeHist(grayFrame, grayFrame);

                            // 3. 检测目标（人脸/上半身，为两种逻辑提供原始数据）
                            var detectBoxes = _detector.DetectMultiScale(
                                image: grayFrame,
                                scaleFactor: 1.1,
                                minNeighbors: _detectMinNeighbors,
                                minSize: _crowdMinDetectSize,
                                maxSize: Size.Empty
                            );
                            // 假设已检测到候选人脸框
                            List<Rectangle> validFaces = new List<Rectangle>();
                            foreach (var faceBox in detectBoxes)
                            {
                                // 提取人脸区域
                                using (Mat faceRegion = new Mat(grayFrame, faceBox))
                                {
                                    // 1. 验证宽高比（人脸通常接近1:1.2）
                                    float aspectRatio = (float)faceBox.Width / faceBox.Height;
                                    if (aspectRatio < 0.8 || aspectRatio > 1.3)
                                        continue; // 宽高比异常，视为误报
                                    //Console.WriteLine($"face WXH = [{faceBox.Width}]X[{faceBox.Height}]");
                                    // 2. 检测眼睛（若人脸框内无眼睛，视为误报）
                                    var eyeDetector = new CascadeClassifier(@"c:\pgina\haarcascade_eye.xml");
                                    var eyes = eyeDetector.DetectMultiScale(faceRegion, 1.1, 3);
                                    //FIXME: change the width to fit the all kinds of situation.
                                    int face_width_min_limit = 90;
                                    if (eyes.Length >= 1 || (aspectRatio >= 0.9 && aspectRatio <= 1.2 && faceBox.Width >= face_width_min_limit))
                                    {
                                        validFaces.Add(faceBox);
                                    }
                                }
                            }
                            // 4. 执行围观检测逻辑
                            ProcessCrowdDetection(validFaces.ToArray());

                            // 5. 执行人员离开检测逻辑
                            ProcessLeaveDetection(validFaces.ToArray());
                        }
                    }

                    // 控制帧处理间隔，降低CPU占用
                    System.Threading.Thread.Sleep(_leaveFrameInterval);
                }
                catch (Exception ex)
                {
                    var errorMsg = $"帧处理错误：{ex.Message}（{DateTime.Now:HH:mm:ss}）";
                    ErrorOccurred?.Invoke(errorMsg);
                    Console.WriteLine(errorMsg);
                    System.Threading.Thread.Sleep(_leaveFrameInterval * 5); // 出错时延长间隔
                }
            }
        }

        /// <summary>
        /// 围观检测逻辑（判断是否≥2人聚集）
        /// </summary>
        private void ProcessCrowdDetection(Rectangle[] detectBoxes)
        {
            lock (_lockObj)
            {
                // 更新当前人数
                _currentPersonCount = detectBoxes.Length;
                bool newIsCrowded = false;

                // 判定是否围观：人数≥阈值 + 任意两人距离≤阈值
                if (_currentPersonCount >= _crowdMinPersonCount)
                {
                    var centers = detectBoxes
                        .Select(box => new System.Drawing.Point(box.X + box.Width / 2, box.Y + box.Height / 2))
                        .ToList();

                    // 检查所有目标对的距离
                    for (int i = 0; i < centers.Count; i++)
                    {
                        for (int j = i + 1; j < centers.Count; j++)
                        {
                            if (CalculateEuclideanDistance(centers[i], centers[j]) <= _crowdMaxDistancePixel)
                            {
                                newIsCrowded = true;
                                break; // 只要有一对满足，即判定为围观
                            }
                        }
                        if (newIsCrowded) break;
                    }
                }

                // 若围观状态变化，触发回调
                if (newIsCrowded != _isCrowded)
                {
                    _isCrowded = newIsCrowded;
                    CrowdStatusChanged?.Invoke(_currentPersonCount, _isCrowded);

                    // 控制台日志
                    var crowdLog = _isCrowded
                        ? $"【围观警告】当前人数：{_currentPersonCount}（{DateTime.Now:HH:mm:ss}）"
                        : $"【围观解除】当前人数：{_currentPersonCount}（{DateTime.Now:HH:mm:ss}）";
                    Console.ForegroundColor = _isCrowded ? ConsoleColor.Red : ConsoleColor.Green;
                    Console.WriteLine(crowdLog);
                    Console.ResetColor();
                }
            }
        }

        /// <summary>
        /// 人员离开检测逻辑（15秒无人则锁定系统）
        /// </summary>
        private void ProcessLeaveDetection(Rectangle[] detectBoxes)
        {
            lock (_lockObj)
            {
                bool hasPerson = detectBoxes.Length > 0;
                double noPersonDurationSec = 0;

                // 若已锁定系统，不再重复判断（除非检测到人员，重置锁定状态）
                if (_hasLockedSystem)
                {
                    if (hasPerson)
                    {
                        _hasLockedSystem = false; // 人员返回，重置锁定状态
                        LeaveStatusChanged?.Invoke(hasPerson, 0, false);
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 人员返回，解除锁定状态");
                        Console.ResetColor();
                    }
                    return;
                }

                // 无人状态处理
                if (!hasPerson)
                {
                    // 记录无人开始时间
                    if (_noPersonStartTime == null)
                    {
                        _noPersonStartTime = DateTime.Now;
                        LeaveStatusChanged?.Invoke(false, 0, false);
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 未检测到人员，开始计时（{LeaveTimeoutMs / 1000}秒后锁定）");
                        Console.ResetColor();
                    }
                    else
                    {
                        // 计算无人持续时间
                        noPersonDurationSec = Math.Round(
                            (DateTime.Now - _noPersonStartTime.Value).TotalSeconds,
                            1
                        );
                        LeaveStatusChanged?.Invoke(false, noPersonDurationSec, false);

                        // 达到阈值，触发系统锁定
                        if (noPersonDurationSec >= LeaveTimeoutMs / 1000)
                        {
                            bool lockSuccess = LockWorkStation();
                            _hasLockedSystem = true;

                            var lockLog = lockSuccess
                                ? $"【系统锁定】无人持续{noPersonDurationSec}秒（{DateTime.Now:HH:mm:ss}）"
                                : $"【锁定失败】无人持续{noPersonDurationSec}秒，权限不足（{DateTime.Now:HH:mm:ss}）";

                            Console.ForegroundColor = lockSuccess ? ConsoleColor.Red : ConsoleColor.Yellow;
                            Console.WriteLine(lockLog);
                            Console.ResetColor();
                            LeaveStatusChanged?.Invoke(false, noPersonDurationSec, true);
                            ErrorOccurred?.Invoke(lockSuccess ? "" : "系统锁定失败，可能缺少权限");
                        }
                        else
                        {
                            // 实时打印无人持续时间（可选，减少日志冗余可注释）
                            // Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 无人持续{noPersonDurationSec}秒");
                        }
                    }
                }
                else
                {
                    // 有人状态：重置计时
                    if (_noPersonStartTime != null)
                    {
                        _noPersonStartTime = null;
                        LeaveStatusChanged?.Invoke(true, 0, false);
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 检测到人员，重置离开计时");
                        Console.ResetColor();
                    }
                }
            }
        }

        /// <summary>
        /// 计算两点间欧氏距离（用于围观检测的聚集判断）
        /// </summary>
        private double CalculateEuclideanDistance(System.Drawing.Point p1, System.Drawing.Point p2)
        {
            int dx = p1.X - p2.X;
            int dy = p1.Y - p2.Y;
            return Math.Sqrt(dx * dx + dy * dy);
        }

        /// <summary>
        /// 释放资源（避免内存泄漏和设备占用）
        /// </summary>
        public void Dispose()
        {
            StopMonitoring();
            _capture?.Dispose();
            _detector?.Dispose();
            GC.SuppressFinalize(this);
            Console.WriteLine("融合版监控资源已释放");
        }

        /// <summary>
        /// 析构函数（防止忘记调用Dispose）
        /// </summary>
        ~CrowdAndLeaveMonitor()
        {
            Dispose();
        }
    }
}